#ifndef OWL_EYE_IMAGE_DB_H
#define OWL_EYE_IMAGE_DB_H

#include "axle/core.h"

#include <map>
#include <list>
#include <queue>

#include "axle/img/image.hpp"
#include "haar.h"

#if defined(SYS_IS_WINDOWS) 
#include <memory>
#else
#include <tr1/memory>
#endif

#ifdef LIBAPI
#else
#ifdef BUILD_SHARED
#define LIBAPI __declspec(dllimport)
#else
#define LIBAPI
#endif
#endif

namespace oe {
typedef int Idx;
typedef int IntId;

enum { kBadId = -1 };

class IntGuid {
public:
  // load from configuration, exit if fail
  IntGuid();
  IntId NewId();  
  bool Check_Error();
  static bool IsValid(IntId id) { return id >= 0; }
private:
  IntId next_id_;
};

enum QueryType { kPicture, kSketch };

// copy not allowed
class LIBAPI ImageDb {
public:
  struct Result {
    Result(IntId id, double score) : img_id(id), score(score) { }
    IntId img_id;
    double score;
    bool operator<(const Result &right) const {
      return score < right.score;
    }
  };

  struct QueryParam {
    QueryParam(int sketch, bool color_only, int n_results)
        : sketch(sketch), color_only(color_only), n_results(n_results) {
    }

    int sketch;
    bool color_only;
    int n_results;
  };

  typedef std::list<Result> ResultList;
  typedef ResultList::iterator ResultIt;

  typedef std::priority_queue<Result> ResultPriorityQueue;
  
  IntId AddImage(const std::string &filename);
  int AddImages(const std::vector<std::string> &filenames, 
                std::vector<IntId> *ids);
  int AddImageFolder(const std::string &dir);

  int Query(const ax::ImageRgbByte &image, const QueryParam &param,
            ResultList &result);

  int Query(IntId img_id, const QueryParam &param, ResultList &result);

  int Query(const std::string &filename, const QueryParam &param,
            ResultList &result);

  const std::string &name() const { return name_; }
  int id() const { return id_; }

  const std::string &GetImageFile(IntId img_id) { return images_[img_id]; }

private:
  ImageDb(int id, const std::string &name);
  ~ImageDb();

  static void Initialize();
private:  
  static const int kDim = 128;
  static const int kImgSize = kDim * kDim;
  static const int kNCoefs = 40;
  static const int kTerribleScore = 99999;

  static const float s_weights[2][6][3];
  static byte s_img_bin[kImgSize];
  static bool s_initialized;

  static IntGuid s_guid;

private:  
  struct DiskSignature {
  public:
    IntId id;
    Idx sigs[3][kNCoefs];
    double avgs[3];
    int width;
    int height;
  };
  struct Signature : public DiskSignature {
  public:
    double score;
  };

  typedef std::list<int> ImgIdList;
  typedef ImgIdList::iterator ImgIdListIt;
  
  typedef std::tr1::shared_ptr<Signature> SigPtr;
  typedef std::map<IntId, SigPtr> SigMap;
  typedef SigMap::iterator SigMapIt;

  typedef std::priority_queue<Signature> SigPriorityQueue;

  typedef std::map<IntId, std::string> ImageMap;
  typedef ImageMap::iterator ImageMapIt;

private:
  SigPtr ExtractSignature(const ax::ImageRgbByte &image);

  const SigPtr GetSignature(IntId img_id) {
    if (sigs_.count(img_id) > 0) return sigs_[img_id];
    return SigPtr();
  }

  int Query(const Signature &sig, const QueryParam &param, 
            ResultList &result);

private:
  int id_;
  const std::string name_;
  
  Haar haar_;
  decimal A_[3][kImgSize];

  ImgIdList img_buckets_[3][2][kImgSize];
  SigMap sigs_;

  ImageMap images_;

  friend class ImageDbServer;
};


class LIBAPI ImageDbServer {
public:     
  typedef std::tr1::shared_ptr<ImageDb> ImageDbPtr;
  typedef std::tr1::shared_ptr<ImageDbServer> ImageDbServerPtr;

  int NewDb(const std::string &name);
  ImageDbPtr GetDb(int db_id) { return dbs_[db_id]; }
  static ImageDbServerPtr GetServer() { return s_server; }

  static void DbDeleter(ImageDb *db) { delete db; }
  static void ServerDeleter(ImageDbServer *server) { delete server; }
private:
  ImageDbServer();
  ~ImageDbServer();

  ImageDbServer(const ImageDbServer &);

private:
  typedef std::map<int, ImageDbPtr> DBMap;
  typedef DBMap::iterator DBMapIt;

  DBMap dbs_;

  static IntGuid s_guid;
  static ImageDbServerPtr s_server;
};

typedef ImageDbServer::ImageDbPtr ImageDbPtr;
typedef ImageDbServer::ImageDbServerPtr ImageDbServerPtr;

} // oe

#endif // OWL_EYE_IMAGE_DB_H

